// 1. 用户访问网页 /a
// 2. 服务器构建二跳网页地址 /b?state&appid 各种参数追加
// 3. 跳到微信授权页，用户主动授权，跳回来到 /a?code
// 4. 通过 code 换取 token code => wechat => access_token, openid
// 5. 通过 token + openid 换取资料 access_token => 用户资料
const rp = require('request-promise')
const qs = require('qs');
const crypto = require('crypto')
const base = 'https://api.weixin.qq.com/sns/'
const api = {
  authorize: 'https://open.weixin.qq.com/connect/oauth2/authorize',
  accessToken: base + 'oauth2/access_token',
  userInfo: base + 'userinfo',
  verifyToken: base + 'auth',
}
module.exports = class WechatOAuth {
  constructor(opts) {
    this.appID = opts.appID
    this.appSecret = opts.appSecret
    this.getOAuthToken = opts.getOAuthToken
    this.saveOAuthToken = opts.saveOAuthToken
  }
  // 封装请求
  async request(options) {
    options = Object.assign({}, options, {
      json: true
    })
    try {
      const res = await rp(options)
      return res
    } catch (err) {
      console.log(err)
    }
  }
  // 详细信息/主动授权： snsapi_userinfo 基本信息/静默授权： snsapi_base
  // 第一步：用户同意授权，获取code
  getAuthorizeURL(target, state = '', scope = 'snsapi_base') {
    return `${api.authorize}?appid=${this.appID}&redirect_uri=${encodeURIComponent(target)}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`;
  }
  // 第二步：通过code换取网页授权access_token snsapi_base式的网页授权流程即到此为止。
  async fetchAuthorizeAccessToken(code, openID) {
    let data = await this.getOAuthToken(openID);
    if (!this.isValideToken(data)) {
      data = await this.UpdateAuthorizeAccessToken(code)
      this.saveOAuthToken(data);
    }
    return data;
  }
  async UpdateAuthorizeAccessToken(code) {
    const url = api.accessToken + qs.stringify({
      appid: this.appID,
      secret: this.appSecret,
      code,
      grant_type: 'authorization_code',
    }, {
      addQueryPrefix: true
    });
    const res = await this.request({
      url
    })
    if (res['access_token']) {
      const hash = crypto.createHash('md5');
      hash.update(res['openid'])
      const now = new Date().valueOf();
      const expires_in = now + (res.expires_in - 10) * 1000;
      res.expires_in = expires_in;
      res.name = hash.digest('hex')
    }
    return res;
  }
  // 拉取用户信息(需scope为 snsapi_userinfo)
  async getUserInfo(token, openID, lang = 'zh_CN') {
    const url = `${api.userInfo}?access_token=${token}&openid=${openID}&lang=${lang}`
    const res = await this.request({
      url
    })
    return res
  }
  // 验证accessToken 是否获取
  isValideToken(data) {
    if (!data || !data.expires_in) {
      return false;
    }
    const expires_in = +data.expires_in;
    const now = +new Date().valueOf();
    if (now < expires_in) {
      return true;
    } else {
      return false;
    }
  }
}
